import os
import re
import logging
import agent    # unused import for dynamic loading

import agent.feature as feature_module  
import agent.domain as domain_module

from importlib import import_module

_domain_class_dict  = {}
_feature_class_dict = {}

_available_domain_object  = {}
_available_feature_object = {}

_unsupported_domain_list = {}
_unsupported_feature_list = {}


logger = logging.getLogger('common')


def search_active_domain():
    """ Search all actived domain, include feature-level

    Returns:
        list : all actived domain name list
    """
    active_domain_list = []
    for domain_name in _available_domain_object.keys():
        active_domain_list.append(domain_name)
    
    for feature_search_name in _available_feature_object.keys():
        domain_name = feature_search_name.split(":")[0]
        active_domain_list.append(domain_name)
    
    return list(set(active_domain_list))
    
        
def search_active_feature_handler(domain):
    """ Search all actived related feature of a domain
    
    """
    feature_handler_list = []
    for feature_search_name, feature_handler in _available_feature_object.items():
        if re.search(r"{}:".format(domain), feature_search_name):
            feature_handler_list.append(feature_handler)
    return feature_handler_list

def search_active_domain_handler(domain):
    if _available_domain_object.__contains__(domain):
        return _available_domain_object[domain]
    else:
        return None

def get_feature_handler(domain_name, feature_name):
    feature_search_name = "{}:{}".format(domain_name, feature_name)
    # Known unsupported feature
    if _unsupported_feature_list.__contains__(feature_search_name):
        raise _unsupported_feature_list[feature_search_name]
    
    # Instantiated feature
    if _available_feature_object.__contains__(feature_search_name):
        return _available_feature_object[feature_search_name]
    
    # Try to instantiate feature object
    if _feature_class_dict.__contains__(feature_search_name):
        try:
            _object = _feature_class_dict[feature_search_name]()
            logger.info("[+] instantiate feature '{}'".format(feature_search_name))
            
        except Exception as e:
            _unsupported_feature_list[feature_search_name] = e
            logger.warning("failed to instantiate '{}': {}".format(
                feature_search_name, e
            ))
            raise e
        
        else:
            _available_feature_object[feature_search_name] = _object
            return _available_feature_object[feature_search_name]
        
    _unsupported_feature_list[feature_search_name] = Exception("unknown domain {}".format(domain_name))
    raise _unsupported_domain_list[domain_name]


def get_domain_handler(domain_name):
    # Known unsupported domain
    if _unsupported_domain_list.__contains__(domain_name):
        raise _unsupported_domain_list[domain_name]
    
    # Instantiated domain
    if _available_domain_object.__contains__(domain_name):
        return _available_domain_object[domain_name]

    # Try to instantiate domain object
    if _domain_class_dict.__contains__(domain_name):
        try:
            _object = _domain_class_dict[domain_name]()
            logger.info("[+] instantiate domain '{}'".format(domain_name))
        
        except Exception as e:
            _unsupported_domain_list[domain_name] = e
            logger.warning("failed to instantiate '{}': {}".format(
                domain_name, e
            ))
            raise e
        
        else:
            _available_domain_object[domain_name] = _object
            return _available_domain_object[domain_name]
    
    _unsupported_domain_list[domain_name] = Exception("unknown domain {}".format(domain_name))
    raise _unsupported_domain_list[domain_name]
    

def load_handler_class():
    """ 
    * Identify all the domain classes and feature classes defined in the domain files and feature files but do not instantiate them.

    """
    feature_class_pattern = re.compile(r"class (.*)\(Feature\)")
    logger.info("search feature file in {}".format(os.path.dirname(feature_module.__file__)))
    
    for feature_file_name in os.listdir(os.path.dirname(feature_module.__file__)):
        # search all file in feature dir
        path = os.path.join(os.path.dirname(feature_module.__file__), feature_file_name)
        if os.path.isdir(path):
            continue
        with open(path) as f:
            content = f.read()
        
        # search feature class by pattern
        if re.search(feature_class_pattern, content):
            try:
                import_module('agent.feature.{}'.format(feature_file_name.split('.')[0]))
                
            except Exception as e:
                logger.warning("fail to import feature file {}: {}".format(feature_file_name, e))
                continue
            
            else:
                feature_class_name = re.search(feature_class_pattern, content).group(1)

                feature_class_obj  = eval('agent.feature.{feature_file_name}.{feature_class_name}'.format(
                    feature_file_name = feature_file_name.split('.')[0],
                    feature_class_name = feature_class_name))
                
                _feature_class_dict["{}:{}".format(
                    feature_class_obj.__domain__, feature_class_obj.__feature__)] = feature_class_obj
                
                logger.info("[+] feature '{}:{}' in {}".format(
                    feature_class_obj.__domain__, feature_class_obj.__feature__, feature_file_name
                ))

        else:
            pass
    
    domain_class_pattern = re.compile(r"class (.*)\(BaseDomain\)")
    intellect_domain_class_pattern = re.compile(r"class (.*)\(BaseBenchmark\)")
    logger.info("search domain file in {}".format(os.path.dirname(domain_module.__file__)))

    for domain_file_name in os.listdir(os.path.dirname(domain_module.__file__)):
        path = os.path.join(os.path.dirname(domain_module.__file__), domain_file_name)
        if os.path.isdir(path):
            continue
        with open(path) as f:
            content = f.read()
        
        if re.search(domain_class_pattern, content):
            try:
                import_module('agent.domain.{}'.format(domain_file_name.split('.')[0]))
                
            except Exception as e:
                logger.warning("fail to import domain file {}: {}".format(domain_file_name, e))
                continue
            
            else:
                domain_class_name = re.search(domain_class_pattern, content).group(1)
                domain_class_obj  = eval('agent.domain.{domain_file_name}.{domain_class_name}'.format(
                    domain_file_name = domain_file_name.split('.')[0],
                    domain_class_name = domain_class_name))
                _domain_class_dict[domain_class_obj.__domain__] = domain_class_obj
                
                logger.info("[+] domain '{}' in {}".format(
                    domain_class_obj.__domain__, domain_file_name
                ))
        elif re.search(intellect_domain_class_pattern, content):
            try:
                import_module('agent.domain.{}'.format(domain_file_name.split('.')[0]))
                
            except Exception as e:
                logger.warning("fail to import domain file {}: {}".format(domain_file_name, e))
                continue
            
            else:
                domain_class_name = re.search(intellect_domain_class_pattern, content).group(1)
                domain_class_obj  = eval('agent.domain.{domain_file_name}.{domain_class_name}'.format(
                    domain_file_name = domain_file_name.split('.')[0],
                    domain_class_name = domain_class_name))
                _domain_class_dict[domain_class_obj.__domain__] = domain_class_obj
                
                logger.info("[+] domain '{}' in {}".format(
                    domain_class_obj.__domain__, domain_file_name
                ))
